<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        button {
            padding: 5px 18px;
        }

        .nums-contaier {
            height: 50px;
            margin: 5px;
            overflow-y: hidden;
        }

        ul {
            float: left;
            list-style: none;
            background-color: lightcoral;
            width: 50px;
            transition: transform .6s;
        }

        li {
            text-align: center;
            line-height: 50px;
            height: 50px;
            background-color: currentColor;
            color: rgb(251, 255, 0);
            animation: colorful 1.8s linear infinite alternate;
        }

        span {
            filter: grayscale(1) contrast(9) invert(1);
        }

        @keyframes colorful {
            to {
                filter: hue-rotate(360deg);
                color: rgb(12, 25, 12);
            }
        }
    </style>
</head>

<body>
    <div class="nums-contaier"></div>
    <button>老东西，爆金币了！！</button></button>

    <script>
        const btn = document.querySelector('button'),
            baseNumLen = 9,
            baseHeight = 50;

        createNums(baseNumLen);
        const uls = document.querySelectorAll('ul');
        const _moveNum = throttle(moveNum, 1);

        btn.onclick = () => {
            let index = 0;
            animation(0, 99999, 7000, (val, end) => {
                // 循环每个数字 对应每一列的`ul`
                for (const s of val.toString()) {
                    // 循环每一列的`ul`元素 如果数字匹配上这一列 则移动
                    for (let i = 0; i < baseNumLen; i++) {
                        /**
                         * 由于`requestAnimationFrame`执行速度过快 导致画面反复拉扯
                         * 所以加了节流 但是最后一次的值 有可能被节流取消
                         * 所以在结束时 通过回调判断 调用常规函数
                         */
                        if (!end && index === i) {
                            _moveNum(uls[i], s);
                        }
                        else if (index === i) {
                            moveNum(uls[i], s);
                        }
                    }
                    index++;
                }
                index = 0;
            });
        };

        /**
         * 输入开始与结束值 根据动画持续时间
         * 传入回调函数 每次渲染前关键帧的差值 
         * @param {Number} from 
         * @param {Number} to 
         * @param {Number} duration 0
         * @param {Function} cb 
         */
        function animation(from, to, duration, cb) {
            to = + to;
            from = +from;

            const st = Date.now(),
                diff = to - from,
                speed = diff / duration;

            function _run() {
                const now = Date.now(),
                    timing = now - st;
                if (timing >= duration) {
                    cb && cb(to, 'end');
                    return;
                }

                const perVal = speed * timing;
                cb && cb(parseInt(from + perVal));
                requestAnimationFrame(_run);
            }

            requestAnimationFrame(_run);
        }

        function createNums(max) {
            const frag = document.createDocumentFragment();
            for (let i = 0; i < max; i++) {
                const ul = document.createElement('ul');
                ul.innerHTML = createLis();
                frag.appendChild(ul);
            }

            document.querySelector('.nums-contaier').appendChild(frag);
        }

        function createLis() {
            const arr = [];
            for (let i = 0; i < 10; i++) {
                arr.push(`<li><span>${i}</span></li></li>`);
            }
            return arr.join('');
        }

        function moveNum(el, num) {
            num = +num;
            el.style.transform = `translateY(${-num * baseHeight}px)`;
        }

        function throttle(fn, duration = 50) {
            let st = Date.now();

            return function () {
                const now = Date.now();
                if (now - st >= duration) {
                    st = now;
                    return fn.apply(this, arguments);
                }
            };
        }
    </script>
</body>

</html>